// Copyright (C) Calum Grant 2008

#include <cppscript>
#include "unit_tests.hpp"


namespace
{
	var A_inc(var self)
	{
		return self["inc_by"](1);
	}

	var A_inc_by(var self, var count)
	{
		return self["count"] += count;
	}

	var C_dec_by(var self, var count)
	{
		return self["count"]-=count;
	}

	var B_dec(var self)
	{
		return self["inc_by"](-1);
	}

	var make_A()
	{
		return object().extend("inc",A_inc)("inc_by",A_inc_by)("count",0);
	}

	var make_B()
	{
		return make_A().extend("dec",B_dec);
	}
}


void test_inheritance()
{
	var a = make_B();
	a["inc"]();
	assert( a["count"] == 1 );
	a["dec"]();
	assert( a["count"] == 0 );
}


void test_attributes()
{
	// Attributes exist on objects only
	var o = object();

	// Retrieving a non-existent attribute
	assert_throws( not_found, (var(object()["no_such_attribute"])) );

	// Assigning to an attribute creates the attribute.
	o["p"] = 12;

	// Retrieving a previously set attribute
	assert( o["p"] == 12 );
	assert( o["p"] );

	// Attributes of attributes
	o["q"] = object();
	o["q"]["r"] = 7;
	o["q"]["s"] = object();
	o["q"]["s"]["t"] = 3;
	assert( o["q"]["s"]["t"] == 3 );
	assert( o["q"]["r"] == 7 );

	// Attributes of attributes named by string, int or var
	o["q"]["10"] = 10;
	assert( o["q"]["10"] == 10 );
	//assert( o["q"][std::string("10")] == 10 );
	assert( o["q"][10] == 10 );
	assert( o["q"][o["q"][10]] == 10 );

	// A self-referential attribute
	o["self"] = o;
	assert( o["self"]["self"]["q"]["s"]["t"] == 3 );

	// Operators on attributes

	// Testing attributes
	assert( o.contains("self") );
	assert( !o.contains("no such attribute") );
}


void test_attribute_operators()
{
	var o = object();
	o["int"] = 0;

	assert( ++o["int"] == 1 );
	assert( o["int"]++ == 1 );

	assert( --o["int"] == 1 );
	assert( o["int"]-- == 1 );

	assert( (o["int"] += 12) == 12 );
	assert( (o["int"] -= 4) == 8 );

	assert( o["int"].as_int() == 8 );

	o["int"] = 12;
	assert( o["int"] + "12" == 24 );
	
	var x=12;

	x += 12;
	o["array"] = array();
	o["array"][0] = 1;
	o["it"] = o["array"].begin();
	assert( *o["array"].begin() == 1 );
	assert( *o["it"] == 1 );
	assert( o["it"]->as_string() == "1" );

	o["int"] = 3;
	assert( o["int"] + o["int"] == 6 );
	assert( o["int"] - o["int"] == 0 );
	assert( o["int"] * o["int"] == 9 );
	assert( o["int"] / o["int"] == 1 );
	assert( o["int"] % 2 == 1 );
	assert( o["int"] << 1 == 6 );
	assert( o["int"] >> 1 == 1 );

	assert( -o["int"] == -3 );
	assert( +o["int"] == 3 );
}


void test_erase_attribute()
{
	// Erasing attributes

	var o = object();
	o["self"] = o;

	o.erase("self");
	assert( !o.contains("self") );

	o["self"] = o;	// Reinstate it
	assert( o.contains("self") );
}


var hw_fn(var self)
{
	return "hw";
}


var hello_fn(var self, var str)
{
	return "Hello " + str.as_string();
}


var counter_reset(var self)
{
	self["count"]=0;
	return var();
}


var counter_inc(var self)
{
	self["count"] = self["count"]+1;
	return var();
}


namespace
{
	void void1(var self) { self["count"] = self["count"]+1; }

	void void2(var self, var a) { self["count"] = self["count"] + a; }

	void void3(var self, var a, var b) { self["count"] = self["count"] + a + b; }

	void void4(var self, var a, var b, var c) { self["count"] = self["count"] + a + b + c; }

	void void5(var self, var a, var b, var c, var d) { self["count"] = self["count"] + a + b + c + d; }

	void void6(var self, var a, var b, var c, var d, var e) { self["count"] = self["count"] + a + b + c + d + e; }

	void void7(var self, var a, var b, var c, var d, var e, var f) { self["count"] = self["count"] + a + b + c + d + e + f; }

	void void8(var self, var a, var b, var c, var d, var e, var f, var g) { self["count"] = self["count"] + a + b + c + d + e + f + g; }

	void void9(var self, var a, var b, var c, var d, var e, var f, var g, var h) { self["count"] = self["count"] + a + b + c + d + e + f + g + h; }

	void void10(var self, var a, var b, var c, var d, var e, var f, var g, var h, var i) { self["count"] = self["count"] + a + b + c + d + e + f + g + h + i; }

	var var1(var self) { self["count"]=self["count"]+1; return 1; }

	var var2(var self, var a)
	{
		self["count"] = self["count"] + a;
		return a;
	}

	var var3(var self, var a, var b)
	{
		var sum = a+b;
		self["count"] = self["count"] + sum;
		return sum;
	}

	var var4(var self, var a, var b, var c)
	{
		var sum = a+b+c;
		self["count"] = self["count"] + sum;
		return sum;
	}

	var var5(var self, var a, var b, var c, var d)
	{
		var sum = a+b+c+d;
		self["count"] = self["count"] + sum;
		return sum;
	}

	var var6(var self, var a, var b, var c, var d, var e)
	{
		var sum = a+b+c+d+e;
		self["count"] = self["count"] + sum;
		return sum;
	}

	var var7(var self, var a, var b, var c, var d, var e, var f)
	{
		var sum = a+b+c+d+e+f;
		self["count"] = self["count"] + sum;
		return sum;
	}

	var var8(var self, var a, var b, var c, var d, var e, var f, var g)
	{
		var sum = a+b+c+d+e+f+g;
		self["count"] = self["count"] + sum;
		return sum;
	}

	var var9(var self, var a, var b, var c, var d, var e, var f, var g, var h)
	{
		var sum = a+b+c+d+e+f+g+h;
		self["count"] = self["count"] + sum;
		return sum;
	}
	var var10(var self, var a, var b, var c, var d, var e, var f, var g, var h, var i)
	{
		var sum = a+b+c+d+e+f+g+h+i;
		self["count"] = self["count"] + sum;
		return sum;
	}
}


void test_methods()
{
	var c=object();

	c["0"] = hw_fn;
	c["1"] = hello_fn;
	assert(c["0"]() == "hw");

	assert( c["1"]("world") == "Hello world" );

	// Test methods on self

	var counter = object().extend
		("count",	0)
		("reset",	counter_reset)
		("inc",		counter_inc);

	var c1 = counter;
	c1["inc"]();
	c1["inc"]();
	assert( c1["count"] == 2 );

	c1["reset"]();
	assert( c1["count"] == 0 );

	// Test more params

	counter["void1"] = void1;
	counter["void2"] = void2;
	counter["void3"] = void3;
	counter["void4"] = void4;
	counter["void5"] = void5;
	counter["void6"] = void6;
	counter["void7"] = void7;
	counter["void8"] = void8;
	counter["void9"] = void9;
	counter["void10"]= void10;

	counter["var1"] = var1;
	counter["var2"] = var2;
	counter["var3"] = var3;
	counter["var4"] = var4;
	counter["var5"] = var5;
	counter["var6"] = var6;
	counter["var7"] = var7;
	counter["var8"] = var8;
	counter["var9"] = var9;
	counter["var10"]= var10;

	counter["void1"]();
	counter["void2"](1);
	assert( counter["void3"](1,2) == var() );
	assert( counter["count"] == 5 );
	counter["void4"](1,2,3);
	counter["void5"](1,2,3,4);
	counter["void6"](1,2,3,4,5);
	assert( counter["count"] == 36 );
	counter["void7"](1,2,3,4,5,6);	// 21
	assert( counter["count"] == 57 );
	counter["void8"](1,2,3,4,5,6,7);	// 28
	assert( counter["count"] == 85 );
	counter["void9"](1,2,3,4,5,6,7,8);	// 36
	assert( counter["count"] == 121 );
	counter["void10"](1,2,3,4,5,6,7,8,9);	// 45
	assert( counter["count"] == 166 );

	counter["reset"]();

	assert( counter["var1"]() == 1 && counter["count"] == 1 );
	assert( counter["var2"](1) == 1 && counter["count"] == 2 );
	assert( counter["var3"](1,2) == 3 && counter["count"] == 5 );
	assert( counter["var4"](1,2,3) == 6 && counter["count"] == 11 );
	assert( counter["var5"](1,2,3,4) == 10 && counter["count"] == 21 );
	assert( counter["var6"](1,2,3,4,5) == 15 && counter["count"] == 36 );
	assert( counter["var7"](1,2,3,4,5,6) == 21 && counter["count"] == 57 );
	assert( counter["var8"](1,2,3,4,5,6,7) == 28 && counter["count"] == 85 );
	assert( counter["var9"](1,2,3,4,5,6,7,8) == 36 && counter["count"] == 121 );
	assert( counter["var10"](1,2,3,4,5,6,7,8,9) == 45 && counter["count"] == 166 );
}


void test_instantiate()
{
	var a = object();
	a["1"] = 1;
	a["2"] = 2;

	var b = +a, c=a;
	assert( b["2"] == 2 );
	c["1"] = 4;
	assert( a["1"] == 4 );
	assert( b["1"] == 1);
}


void inc(var self)
{
	self["count"] = self["count"]+1;
}


namespace
{
	void n0() { }
	void n1(var) { }
	void n2(var,var) { }
	void n3(var,var,var) { }
	void n4(var,var,var,var) { }
	void n5(var,var,var,var,var) { }
	void n6(var,var,var,var,var,var) { }
	void n7(var,var,var,var,var,var,var) { }
	void n8(var,var,var,var,var,var,var,var) { }
	void n9(var,var,var,var,var,var,var,var,var) { }
	void n10(var,var,var,var,var,var,var,var,var,var) { }

	var s0() { return 2; }
	var s1(var a0) { return a0; }
	var s2(var a0, var a1) { return a0+a1; }
	var s3(var a0, var a1, var a2) { return a0+a1+a2; }
	var s4(var a0, var a1, var a2, var a3) { return a0+a1+a2+a3; }
	var s5(var a0, var a1, var a2, var a3, var a4) { return a0+a1+a2+a3+a4; }
	var s6(var a0, var a1, var a2, var a3, var a4, var a5) { return a0+a1+a2+a3+a4+a5; }
	var s7(var a0, var a1, var a2, var a3, var a4, var a5, var a6) { return a0+a1+a2+a3+a4+a5+a6; }
	var s8(var a0, var a1, var a2, var a3, var a4, var a5, var a6, var a7) { return a0+a1+a2+a3+a4+a5+a6+a7; }
	var s9(var a0, var a1, var a2, var a3, var a4, var a5, var a6, var a7, var a8) { return a0+a1+a2+a3+a4+a5+a6+a7+a8; }
	var s10(var a0, var a1, var a2, var a3, var a4, var a5, var a6, var a7, var a8, var a9) { return a0+a1+a2+a3+a4+a5+a6+a7+a8+a9; }
}

enable_pickle( n0 );
enable_pickle( n1 );
enable_pickle( s10 );


void test_function()
{
	// Function basics
	assert( !var(n0).impl().shared_var() );
	assert( var(n0).class_name() == "function" );
	assertx( var(n0) == var(n0) );
	assert( var(n0).impl().comparison_index() == cmp_function_v0 );
	assert( var(n1) > object() );
	assert( var(n1) < bind(n1, 1) );
	assertx( var(n1) != var(n2) );
	assertx( var(n1) == +var(n1) );
	var x = object();
	x["x"] = var(n1);

	assertx( unpickle(pickle(n0)) == var(n0) );

	assert( unpickle(pickle(n0)) == var(n0) );
	assert( unpickle(pickle(n1)) == var(n1) );
	assert( unpickle(pickle(s10)) == var(s10) );
	assert_throws( exception, pickle(s9) );

	// Function conversion
	assert( var(n0).as_string() == "<<function>>" );
	assert( var(n0).as_wstring() == L"<<function>>" );
	assert( !var(n0) );
	assert( var(n0).as_int() == 0 );
	assert( var(n0).as_double() == 0.0 );

	// function.max_args
	assert( var(n0).max_args() == 0 );
	assert( var(n1).max_args() == 1 );
	assert( var(n2).max_args() == 2 );
	assert( var(n3).max_args() == 3 );
	assert( var(n4).max_args() == 4 );
	assert( var(n5).max_args() == 5 );
	assert( var(n6).max_args() == 6 );
	assert( var(n7).max_args() == 7 );
	assert( var(n8).max_args() == 8 );
	assert( var(n9).max_args() == 9 );
	assert( var(n10).max_args() == 10 );
	assert( var(s0).max_args() == 0 );
	assert( var(s1).max_args() == 1 );
	assert( var(s2).max_args() == 2 );
	assert( var(s3).max_args() == 3 );
	assert( var(s4).max_args() == 4 );
	assert( var(s5).max_args() == 5 );
	assert( var(s6).max_args() == 6 );
	assert( var(s7).max_args() == 7 );
	assert( var(s8).max_args() == 8 );
	assert( var(s9).max_args() == 9 );
	assert( var(s10).max_args() == 10 );

	test_default_container( n0 );
	test_default_iterator( n0 );
	test_default_assignment( n0 );

	// Test operators
	assert_throws( not_supported, var(n0) + 1 );	
	assert_throws( not_supported, var(n0) - 1 );	
	assert_throws( not_supported, var(n0) * 1 );	
	assert_throws( not_supported, var(n0) / 1 );	
	assert_throws( not_supported, var(n0) % 1 );	
	assert_throws( not_supported, -var(n0) );	
	assert_throws( not_supported, ~var(n0) );	
	assert_throws( not_supported, var(n0) << 1 );	
	assert_throws( not_supported, var(n0) >> 1 );	
	assert_throws( not_supported, var(n0) & 1 );	
	assert_throws( not_supported, var(n0) | 1 );	
	assert_throws( not_supported, var(n0) ^ 1 );	


	// Test call
	(var(n0)());
	(var(n1))(1);
	(var(n2))(1,2);
	(var(n3))(1,2,3);
	(var(n4))(1,2,3,4);
	(var(n5))(1,2,3,4,5);
	(var(n6))(1,2,3,4,5,6);
	(var(n7))(1,2,3,4,5,6,7);
	(var(n8))(1,2,3,4,5,6,7,8);
	(var(n9))(1,2,3,4,5,6,7,8,9);
	(var(n10))(1,2,3,4,5,6,7,8,9,10);

	assert( var(s0)() == 2 );
	assert( var(s1)(1) == 1 );
	assert( var(s2)(1,2) == 3 );
	assert( var(s3)(1,2,3) == 6 );
	assert( var(s4)(1,2,3,4) == 10 );
	assert( var(s5)(1,2,3,4,5) == 15 );
	assert( var(s6)(1,2,3,4,5,6) == 21 );
	assert( var(s7)(1,2,3,4,5,6,7) == 28 );
	assert( var(s8)(1,2,3,4,5,6,7,8) == 36 );
	assert( var(s9)(1,2,3,4,5,6,7,8,9) == 45 );
	assert( var(s10)(1,2,3,4,5,6,7,8,9,10) == 55 );
}


void test_void_methods()
{
	var A = object();
		A["inc"] = inc;
		A["count"] = 0;

	var a=+A;

	a["inc"]();
	assert(a["count"] == 0);
	assert(A["count"] == 1);
}


void test_pickle(const var & v)
{
	assert( unpickle(pickle(v)) == v );
}


var sum1(var a) { return a; }
var sum2(var a, var b) { return a+b; }
var sum3(var a, var b, var c) { return a+b+c; }
var sum4(var a, var b, var c, var d) { return a+b+c+d; }
var sum5(var a, var b, var c, var d, var e) { return a+b+c+d+e; }
var sum6(var a, var b, var c, var d, var e, var f) { return a+b+c+d+e+f; }
var sum7(var a, var b, var c, var d, var e, var f, var g) { return a+b+c+d+e+f+g; }
var sum8(var a, var b, var c, var d, var e, var f, var g, var h) { return a+b+c+d+e+f+g+h; }
var sum9(var a, var b, var c, var d, var e, var f, var g, var h, var i) { return a+b+c+d+e+f+g+h+i; }
var sum10(var a, var b, var c, var d, var e, var f, var g, var h, var i, var j) { return a+b+c+d+e+f+g+h+i+j; }

enable_pickle(sum4)
enable_pickle(sum10)


void test_bind()
{
	// Construction
	var c = bind(sum1, 1);

	// Test basics
	assert( c.impl().shared_var() );
	assert( c.class_name() == "bind" );
	assert( c > var(sum1) );

	// Test conversion
	assert( c.as_string() == "1.<<bind>>" );
	assert( c.as_wstring() == L"1.<<bind>>" );
	assert( !c );
	assert( c.as_int() == 0 );
	assert( c.as_double() == 0.0 );

	test_default_container( c );
	test_default_iterator( c );
	test_default_assignment( c );

	// Test the bind function
	assert( bind(sum1, 1)() == 1 );
	assert( bind(sum1, 1).max_args() == 0 );

	assert( var(sum2)(2,3) == 5 );
	assert_throws( not_supported, (var(sum2)(2,3,4)) );

	assert( bind(sum3,1)(2,4) == 7 );
	assert( bind(sum3,1,2)(4) == 7 );
	assert( bind(sum3, 1,2,4)() == 7 );
	assert( bind(sum3, 1).max_args() == 2 );
	assert_throws( not_supported, (var(sum3)(1,2,3,4)) );

	assert( var(sum2)("Hello ","world!") == "Hello world!" );

	assert( bind(sum4, 1)(2,3,4) == 10 );
	assert( bind(sum4, 1,2)(3,4) == 10 );
	assert( bind(sum4, 1,2,3)(4) == 10 );
	assert( bind(sum4, 1,2,3,4)() == 10 );
	assert( bind(sum4, 1,2,3,4).max_args() == 0 );
	assert_throws( too_many_arguments, bind(sum4, 1,2,3,4,5) );
	assert_throws( not_supported, (var(sum4)(1,2,3)(3,4)) );

	assert( bind(sum5,1)(2,3,4,5) == 15 );
	assert( bind(sum5,1,2)(3,4,5) == 15 );
	assert( bind(sum5,1,2,3)(4,5) == 15 );
	assert( bind(sum5,1,2,3,4)(5) == 15 );
	assert( bind(sum5,1,2,3,4,5)() == 15 );
	assert_throws( not_supported, (bind(sum5,1,2,3,4)(3,4)) );

	assert( bind(sum6,1)(2,3,4,5,6) == 21 );
	assert( bind(sum6,1,2)(3,4,5,6) == 21 );
	assert( bind(sum6,1,2,3)(4,5,6) == 21 );
	assert( bind(sum6,1,2,3,4)(5,6) == 21 );
	assert( bind(sum6,1,2,3,4,5)(6) == 21 );
	assert( bind(sum6,1,2,3,4,5,6)() == 21 );
	assert( bind(sum6,1).max_args() == 5 );
	assert_throws( not_supported, (bind(sum6,1,2,3,4,5)(3,4)) );
	assert_throws( too_many_arguments, bind(sum6,1,2,3,4,5,6,7) );

	assert( bind(sum7,1)(2,3,4,5,6,7) == 28 );
	assert( bind(sum7,1,2)(3,4,5,6,7) == 28 );
	assert( bind(sum7,1,2,3)(4,5,6,7) == 28 );
	assert( bind(sum7,1,2,3,4)(5,6,7) == 28 );
	assert( bind(sum7,1,2,3,4,5)(6,7) == 28 );
	assert( bind(sum7,1,2,3,4,5,6)(7) == 28 );
	assert( bind(sum7,1,2,3,4,5,6,7)() == 28 );
	assert_throws( too_many_arguments, bind(sum7,1,2,3,4,5,6,3,4) );

	assert( bind(sum8,1)(2,3,4,5,6,7,8) == 36 );
	assert( bind(sum8,1,2)(3,4,5,6,7,8) == 36 );
	assert( bind(sum8,1,2,3)(4,5,6,7,8) == 36 );
	assert( bind(sum8,1,2,3,4)(5,6,7,8) == 36 );
	assert( bind(sum8,1,2,3,4,5)(6,7,8) == 36 );
	assert( bind(sum8,1,2,3,4,5,6)(7,8) == 36 );
	assert( bind(sum8,1,2,3,4,5,6,7)(8) == 36 );
	assert( bind(sum8,1,2,3,4,5,6,7,8)() == 36 );
	assert_throws( not_supported, bind(sum8,1,2,3,4,5,6,7)(3,4) );

	assert( bind(sum9,1)(2,3,4,5,6,7,8,9) == 45 );
	assert( bind(sum9,1,2)(3,4,5,6,7,8,9) == 45 );
	assert( bind(sum9,1,2,3)(4,5,6,7,8,9) == 45 );
	assert( bind(sum9,1,2,3,4)(5,6,7,8,9) == 45 );
	assert( bind(sum9,1,2,3,4,5)(6,7,8,9) == 45 );
	assert( bind(sum9,1,2,3,4,5,6)(7,8,9) == 45 );
	assert( bind(sum9,1,2,3,4,5,6,7)(8,9) == 45 );
	assert( bind(sum9,1,2,3,4,5,6,7,8)(9) == 45 );
	assert( bind(sum9,1,2,3,4,5,6,7,8,9)() == 45 );
	assert_throws( too_many_arguments, bind(sum9,1,2,3,4,5,6,7,8,9,10) );

	assert( bind(sum10,1)(2,3,4,5,6,7,8,9,10) == 55 );
	assert( bind(sum10,1,2)(3,4,5,6,7,8,9,10) == 55 );
	assert( bind(sum10,1,2,3)(4,5,6,7,8,9,10) == 55 );
	assert( bind(sum10,1,2,3,4)(5,6,7,8,9,10) == 55 );
	assert( bind(sum10,1,2,3,4,5)(6,7,8,9,10) == 55 );
	assert( bind(sum10,1,2,3,4,5,6)(7,8,9,10) == 55 );
	assert( bind(sum10,1,2,3,4,5,6,7)(8,9,10) == 55 );
	assert( bind(sum10,1,2,3,4,5,6,7,8)(9,10) == 55 );
	assert( bind(sum10,1,2,3,4,5,6,7,8,9)(10) == 55 );
	assert( bind(sum10,1,2,3,4,5,6,7,8,9)(10) == 55 );
	assert( bind(sum10,1,2,3,4,5,6,7,8,9,10)() == 55 );
	assert_throws( too_many_arguments, bind(sum10,1,2,3,4,5,6,7,8,9)(3,4) );

	// Test function pickle
	test_pickle(sum4);

	//  bind comparison
	assert( bind(sum1, 1) != 1 );
	assert( bind(sum1, 1) == bind(sum1, 1) );
	assert( bind(sum1, 1) != bind(sum2, 1) );
	assert( bind(sum1, 1) < bind(sum1, 2) );
	assert( bind(sum2, 1) < bind(sum2, 1, 2) );

	// Test bind pickle
	test_pickle(bind(sum10, 2));
	test_pickle(bind(sum10, 1, 2));
	test_pickle(bind(sum10, 1, 2, 3));
	test_pickle(bind(sum10, 1, 2, 3, 4));
	test_pickle(bind(sum10, 1, 2, 3, 4, 5));
	test_pickle(bind(sum10, 1, 2, 3, 4, 5, "6"));
	test_pickle(bind(sum10, 1, 2, 3, 4, 5, "6", "7"));
	test_pickle(bind(sum10, 1, 2, 3, 4, 5, "6", "7", 8.0));
	test_pickle(bind(sum10, 1, 2, 3, 4, 5, "6", "7", 8.0, 9));
	test_pickle(bind(sum10, 1, 2, 3, 4, 5, "6", "7", 8.0, 9, 10));

	assert( bind(sum10, 1).as_string() == "1.<<bind>>" );
	assert( bind(sum10, 1).as_wstring() == L"1.<<bind>>" );
}


var get_fish(var o) 
{ 
	return o["fish"]; 
}


void test_extend()
{
	var o = object().extend("fish", 12);
	assert( o["fish"] == 12 );
	o.extend("get", get_fish);
	assert( o["get"]() == 12 );

	var o2 = o.extend("x", "x")("y", "y");
	assert( o2["x"] == "x" );
	assert( o2["y"] == "y" );
	assert( o2 == o );

	var o3 = o2.extend("a",1)("b",2.0)("c",L"c");
	assert( o3["a"] == 1 );
	assert( o3["b"] == 2.0 );
	assert( o3["c"] == L"c" );
	assert( o3 == o2 );
}


namespace
{
	var xf1(var list)
	{
		assert(list[0] == list.size());
		for(var i=1; i<list.size(); ++i)
			assert(list[i] == i);
		return 12;
	}

	var xf2(var list)
	{
		assert(list.empty());
		return 13;
	}
}

enable_pickle(xf2)


void test_varargs()
{
	var va = varargs(xf2);

	// varargs.shared
	assert( va.impl().shared_var() );

	// varargs.class_name
	assert( va.class_name() == "varargs" );

	// varargs.comparison_index
	assert( va.impl().comparison_index() == cmp_varargs );

	// varargs.compare
	assert( varargs(xf2) == varargs(xf2) );
	assert( varargs(xf1) != varargs(xf2) );
	assert( varargs(xf1) < varargs(xf2) || varargs(xf1) > varargs(xf2) );

	// varargs.clone
	assert( +va == va );

	// varargs.as_int
	assert( va.as_int() == 0 );

	// varargs.as_double
	assert( va.as_double() == 0.0 );

	// varargs.as_bool
	assert( !va );

	// varargs.as_ptr
	assert( !va.impl().as_ptr() );

	// varargs.container 
	test_default_container( va );
	test_default_iterator( va );
	test_default_operators( va );
	test_default_assignment( va );

	// varargs.call
	assert( varargs(xf2)() == 13 );
	assert( varargs(xf1)(1) == 12 );
	assert( varargs(xf1)(2,1) == 12 );
	assert( varargs(xf1)(3,1,2) == 12 );
	assert( varargs(xf1)(4,1,2,3) == 12 );
	assert( varargs(xf1)(5,1,2,3,4) == 12 );
	assert( varargs(xf1)(6,1,2,3,4,5) == 12 );
	assert( varargs(xf1)(7,1,2,3,4,5,6) == 12 );
	assert( varargs(xf1)(8,1,2,3,4,5,6,7) == 12 );
	assert( varargs(xf1)(9,1,2,3,4,5,6,7,8) == 12 );
	assert( varargs(xf1)(10,1,2,3,4,5,6,7,8,9) == 12 );

	assert( varargs(xf1).max_args() == 10 );

	// varargs.as_string
	assert( varargs(xf2).as_string() == "varargs(<<function>>)" );
	assert( varargs(xf2).as_wstring() == L"varargs(<<function>>)" );

	// varargs.pickle
	assert( unpickle(pickle(va)) == va );
}


namespace Counter
{
	void inc(var self) { ++self["count"]; }

	void reset(var self) { self["count"]=0; }

	void dec(var self) { self["count"]=self["count"]-1; }

	enable_pickle(inc);
	enable_pickle(reset);
	enable_pickle(dec);

	var counter()
	{
		return object().extend("inc", inc)("dec", dec)("reset", reset)("count", 0);
	}
}


void test_pickle_object()
{
	// object
	var o = object();
		o["a"] = 12;
		o["b"] = "foo";
		o["c"] = o;
	var p = unpickle(pickle(o));
	assert(p.size() == 3);
	assert(p["b"] == "foo");


	var c1 = Counter::counter();
	assert( c1["count"] == 0 );
	c1["inc"]();
	assert( c1["count"] == 1 );

	var c2 = unpickle(pickle(c1));
	c2["inc"]();
	assert( c2["count"] == 2 );

	// More complex, self referential with closures

	var c3 = Counter::counter();

	c3["inc"] = c2["dec"];	// Method refers to a different object (and decrements!)
	c3["other_counter"] = c2;
	c3["first_counter"] = c3;
	c3["count"] = 5;

	var c4 = unpickle(pickle(c3));
	c4["inc"]();	// Actually call "dec"

	assert( c4["count"] == 5 );
	assert( c4["other_counter"]["count"] == 1 );
}


namespace
{
	void hello_this_is_a_function()
	{
		assert( stack_trace().front().as_string() == "hello_this_is_a_function" );
	}
}


void test_named_functor()
{
	// !! This may be implemented next version
	// func(hello_this_is_a_function)();
}
